package cn.texous.easytalk.websocket.tio;

import cn.texous.easytalk.commonutil.constant.RedisKey;
import cn.texous.easytalk.commonutil.constant.ResultCode;
import cn.texous.easytalk.commonutil.exception.BusinessException;
import cn.texous.easytalk.commonutil.model.redis.RedisUser;
import cn.texous.easytalk.commonutil.model.redis.RedisUserDetailInfo;
import cn.texous.easytalk.commonutil.util.GsonUtils;
import cn.texous.easytalk.commonutil.util.RedisKeyUtil;
import cn.texous.easytalk.websocket.context.EtMessageHandlerContext;
import cn.texous.easytalk.websocket.model.entity.MsgType;
import cn.texous.starter.redis.RedisClient;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.tio.common.starter.annotation.TioServerMsgHandler;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.http.common.HttpRequest;
import org.tio.http.common.HttpResponse;
import org.tio.http.common.HttpResponseStatus;
import org.tio.websocket.common.WsRequest;
import org.tio.websocket.server.handler.IWsMsgHandler;

import java.util.Optional;

/**
 * insert description here
 *
 * @author Showa.L
 * @since 2019/8/28 14:23
 */
@Slf4j
@TioServerMsgHandler
public class EtWsMsgHandler implements IWsMsgHandler {

    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private EtMessageHandlerContext etMessageHandlerContext;


    /**
     * 握手时走这个方法，业务可以在这里获取cookie，request参数等
     *
     * @param request        request
     * @param httpResponse   httpResponse
     * @param channelContext channelContext
     * @return HttpResponse
     */
    @Override
    public HttpResponse handshake(HttpRequest request,
                                  HttpResponse httpResponse,
                                  ChannelContext channelContext) {
        String token = request.getParam("token");
        try {
            RedisUser user = Optional.ofNullable(token)
                    .filter(RedisClient::exists)
                    .map(t -> RedisKeyUtil.getKey(RedisKey.USER_LOGIN_TOKEN, t))
                    .map(RedisClient::get)
                    .map(s -> GsonUtils.fromJson(s, RedisUser.class))
                    .orElseThrow(() -> new BusinessException(ResultCode.USER_UNLOGIN_ERROR));

            //绑定用户
            Tio.bindUser(channelContext, user.getCode());

            //绑定群组
            Optional.ofNullable(user.getCode())
                    .map(code -> RedisKeyUtil.getKey(RedisKey.USER_DETAIL_INFO, code))
                    .map(RedisClient::get)
                    .map(info -> GsonUtils.fromJson(info, RedisUserDetailInfo.class))
                    .map(RedisUserDetailInfo::getGroups)
                    .ifPresent(redisChatGroups ->
                            redisChatGroups.forEach(redisChatGroup ->
                                    Tio.bindGroup(channelContext, redisChatGroup.getChatGroupCode())
                            ));

        } catch (Exception e) {
            log.error("handshake error", e);
            httpResponse.setStatus(HttpResponseStatus.getHttpStatus(401));
        }
        return httpResponse;
    }

    /**
     * @param httpRequest    httpRequest
     * @param httpResponse   httpResponse
     * @param channelContext channelContext
     * @throws Exception Exception
     * @author tanyaowu tanyaowu
     */
    @Override
    public void onAfterHandshaked(HttpRequest httpRequest,
                                  HttpResponse httpResponse,
                                  ChannelContext channelContext) throws Exception {

    }

    /**
     * 字节消息（binaryType = arraybuffer）过来后会走这个方法
     */
    @Override
    public Object onBytes(WsRequest wsRequest, byte[] bytes,
                          ChannelContext channelContext) throws Exception {
        return null;
    }

    /**
     * 当客户端发close flag时，会走这个方法
     */
    @Override
    public Object onClose(WsRequest wsRequest, byte[] bytes,
                          ChannelContext channelContext) throws Exception {
        Tio.remove(channelContext, "receive close flag");
        return null;
    }

    /**
     * 字符消息（binaryType = blob）过来后会走这个方法
     *
     * @param wsRequest      wsRequest
     * @param text           text
     * @param channelContext channelContext
     * @return obj
     */
    @Override
    public Object onText(WsRequest wsRequest, String text, ChannelContext channelContext) {
        try {
            MsgType msgType = objectMapper.readValue(text, MsgType.class);
            System.out.println(text);
            log.info("received msg: {}", text);
            return etMessageHandlerContext
                    .loadByType(msgType.getType())
                    .handler(text, channelContext);
        } catch (Exception e) {
            log.error("onText message error", e);
        }
        return null;
    }

}
